/*
 * Balance térmico de invierno
 *
 * Juan Manuel Mouriz <jmouriz@slowsoft.com.ar>
 * http://www.slowsoft.com.ar/
 *
 * gcc -O3 -o balance balance.c $(pkg-config --cflags --libs gtk+-2.0 sqlite3)
 * fix-interface.pl balance.xml balance.ui
 */

/* TODO Pasar a la interfaz todas definiciones que sean posibles para abstraer este código sólo al comportamiento de la IU */
/* TODO Unificar los botones Añadir cerramiento + Añadir ventana y Quitar cerramiento + Quitar ventana porque la ventana es un cerramiento */

#include <stdarg.h>
#include <stdlib.h>

#include <gtk/gtk.h>
#include <sqlite3.h>

#ifdef G_OS_WIN32
#include <windows.h>
#endif

#define MAXIMUM_WINDOWS 3		/* Indica la cantidad máxima de ventanas que puede tener un cerramiento horizontal */
#define DATABASE	"balance.db"	/* Indica el nombre del archivo de base de datos */
#define INTERFACE	"balance.ui"	/* Indica el nombre del archivo que tiene la definición de la interfaz con el usuario */

static sqlite3 *database_handler;
static gboolean already_tags = 0;	/* Indica si las marcas de texto ya están creadas */

enum
{
  GENERAL_TAB,
  LOCATIONS_TAB,
  MATERIALS_TAB,
  ENCLOSURES_TAB,
  ENVIRONMENTS_TAB,
  SUMMARY_TAB
};

/* ENVIRONMENTS */
enum
{
  ENVIRONMENTS_ENVIRONMENT_COLUMN,
  ENVIRONMENTS_GROUP_COLUMN,
  ENVIRONMENTS_WIDTH_COLUMN,
  ENVIRONMENTS_HEIGHT_COLUMN,
  ENVIRONMENTS_AREA_COLUMN,
  ENVIRONMENTS_HEAT_COLUMN,
  ENVIRONMENTS_SAME_FLOOR_CEIL_COLUMN,			/* Indica que los valores del techo son iguales a los del piso */

  ENVIRONMENTS_WINDOWS_COLUMN,				/* Indica la cantidad de ventanas que tiene el cerramiento */

  ENVIRONMENTS_IS_HORIZONTAL_COLUMN,			/* Indica si el cerramiento es una ventana */
  ENVIRONMENTS_IS_VERTICAL_COLUMN,			/* Indica si el cerramiento es una ventana */
  ENVIRONMENTS_IS_WINDOW_COLUMN,			/* Indica si el cerramiento es una ventana */

  ENVIRONMENTS_GROUP_EDITABLE_COLUMN,
  ENVIRONMENTS_WIDTH_EDITABLE_COLUMN,
  ENVIRONMENTS_HEIGHT_EDITABLE_COLUMN,
  ENVIRONMENTS_SAME_FLOOR_CEIL_ACTIVATABLE_COLUMN,	/* Indica que los valores del techo son iguales a los del piso */

  ENVIRONMENTS_COLUMNS
};

/* ENCLOSURES */
enum
{
  ENCLOSURES_ENCLOSURE_COLUMN,
  ENCLOSURES_TYPE_COLUMN,
  ENCLOSURES_THICKNESS_COLUMN,
  ENCLOSURES_TRANSMITTANCE_COLUMN,
  ENCLOSURES_BOTH_SIDES_COLUMN,				/* Indica si el material está a ambos lados del muro */

  ENCLOSURES_TYPE_EDITABLE_COLUMN,
  ENCLOSURES_THICKNESS_EDITABLE_COLUMN,
  ENCLOSURES_TRANSMITTANCE_EDITABLE_COLUMN,
  ENCLOSURES_BOTH_SIDES_ACTIVATABLE_COLUMN,

  ENCLOSURES_COLUMNS
};

/* MATERIALS */
enum
{
  MATERIALS_MATERIAL_COLUMN,
  MATERIALS_DENSITY_COLUMN,
  MATERIALS_CONDUCTIVITY_COLUMN,

  MATERIALS_COLUMNS
};

/* LOCATIONS */
enum
{
  LOCATIONS_LOCATION_COLUMN,
  LOCATIONS_MINIMUM_COLUMN,
  LOCATIONS_MAXIMUM_COLUMN,

  LOCATIONS_COLUMNS
};

/* FUNCIONES */

gboolean
is_parent (const gchar *path)
{
  g_return_val_if_fail (path, FALSE);

  gint position = -1;

  while (path[++position] != '\0')
    if (path[position] == ':')
      return (FALSE);

  return (TRUE);
}

gboolean
is_window (const gchar *path)
{
  g_return_val_if_fail (path, FALSE);

  gint position = -1;
  gint level = 0;

  while (path[++position] != '\0')
    if (path[position] == ':')
      level++;

  if (level == 2)
    return (TRUE);

  return (FALSE);
}

void
show_error_dialog (const gchar *title, const gchar *message, const gchar *error)
{
    GtkWidget *dialog;

    dialog = gtk_message_dialog_new_with_markup (NULL, 0, GTK_MESSAGE_ERROR,
                                                 GTK_BUTTONS_CLOSE,
                                                 error ? "<span weight=\"heavy\" size=\"larger\">%s</span>" : "%s", message);

    if (error)
      gtk_message_dialog_format_secondary_markup (GTK_MESSAGE_DIALOG (dialog), "%s", error);

    gtk_window_set_title (GTK_WINDOW (dialog), title ? title : "Error");
    gdk_beep ();
    gtk_dialog_run (GTK_DIALOG (dialog));
    gtk_widget_destroy (dialog);
}

gboolean
sql_statement (sqlite3_stmt **statement, const gchar *format, ...) /* get one and only one (never none) row */
{
  gchar *query;
  va_list values;
  int error;
  gboolean status;

  va_start (values, format);
  query = g_strdup_vprintf (format, values);
  va_end (values);

  g_return_val_if_fail (query, FALSE);

  if ((error = sqlite3_prepare (database_handler, query, -1, statement, NULL)) != SQLITE_OK) {
    gchar *message = g_strdup_printf ("<i>%s</i> al ejecutar el comando <i>%s</i>", sqlite3_errmsg (database_handler), query);
    show_error_dialog ("Error SQL", "Error al ejecutar el comando SQL", message);
    g_free (message);
    status = FALSE;
  } else if (error = sqlite3_step (*statement) == SQLITE_ERROR) {
    gchar *message = g_strdup_printf ("<i>%s</i> al ejecutar el comando <i>%s</i>", sqlite3_errmsg (database_handler), query);
    show_error_dialog ("Error SQL", "Error al ejecutar el comando SQL", message);
    g_free (message);
    status = FALSE;
  } else {
    status = TRUE;
  }

  g_free (query);

  return (status);
}

static int
set_row (void *model, int columns, char **values, char **headers)
{
  GtkTreeIter iter;

  if (GTK_IS_TREE_STORE (model))
    gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL);
  else
    gtk_list_store_append (GTK_LIST_STORE (model), &iter);

  int column;
  for (column = 0; column < columns; column++)
    if (values[column])
      if (GTK_IS_TREE_STORE (model))
        gtk_tree_store_set (GTK_TREE_STORE (model), &iter, column, values[column], -1);
      else
        gtk_list_store_set (GTK_LIST_STORE (model), &iter, column, values[column], -1);
 
  return (0);
}

static void
set_data_list (GtkTreeModel *model, const gchar* query)
{
  char *error = 0;
  if (sqlite3_exec (database_handler, query, set_row, (void *) model, &error) != SQLITE_OK) {
    gchar *message = g_strdup_printf ("<i>%s</i> al ejecutar el comando <i>%s</i>", error, query);
    show_error_dialog ("Error SQL", "Error al ejecutar el comando SQL", message);
    g_free (message);
    sqlite3_free (error);
  }
}

static void
set_environments_tree (GtkTreeModel *model, const gchar* query)
{
  GtkTreeIter child;
  GtkTreeIter parent;
  GtkTreeIter iter;
  sqlite3_stmt *statement;
  gchar *value;
  gchar *group = NULL;
  gint column;
  gint target;
  gint editable;

  if (sqlite3_prepare (database_handler, query, -1, &statement, NULL) != SQLITE_OK) {
    gchar *message = g_strdup_printf ("<i>%s</i> al ejecutar el comando <i>%s</i>", sqlite3_errmsg (database_handler), query);
    show_error_dialog ("Error SQL", "Error al ejecutar el comando SQL", message);
    g_free (message);
    return;
  }

  while (sqlite3_step (statement) != SQLITE_DONE) {
    value = (gchar *) sqlite3_column_text (statement, 0);

    /* if are distinct, add new group */
    if (g_strcmp0 (group, value)) {
      gtk_tree_store_append (GTK_TREE_STORE (model), &parent, NULL);

      if (group)
        g_free (group);

      group = g_strdup (value);
    }

    gtk_tree_store_append (GTK_TREE_STORE (model), &child, &parent);

    for (column = 0; column < 13; column++) {
      value = (gchar *) sqlite3_column_text (statement, column);

      /*
       * 00:	ambiente:	ENVIRONMENTS_ENVIRONMENT_COLUMN:	P
       * 01:	cerramiento:	ENVIRONMENTS_ENVIRONMENT_COLUMN:	C
       * 02:	exposicion:	ENVIRONMENTS_GROUP_COLUMN:		P
       * 03:	grupo:		ENVIRONMENTS_GROUP_COLUMN:		C
       * 04:	x:		ENVIRONMENTS_WIDTH_COLUMN:		P
       * 05:	y:		ENVIRONMENTS_HEIGHT_COLUMN:		P
       * 06:	x:		ENVIRONMENTS_WIDTH_COLUMN:		C
       * 07:	y:		ENVIRONMENTS_HEIGHT_COLUMN:		C
       * 08:	volumen:	ENVIRONMENTS_AREA_COLUMN:		P
       * 09:	superficie:	ENVIRONMENTS_AREA_COLUMN:		C
       * 10:	doble:		ENVIRONMENTS_SAME_FLOOR_CEIL_COLUMN:	C
       * 11:	balance:	ENVIRONMENTS_HEAT_COLUMN:		P
       * 12:	calor:		ENVIRONMENTS_HEAT_COLUMN:		C
       */

      switch (column) {
        case 0:
          target = ENVIRONMENTS_ENVIRONMENT_COLUMN;
	  editable = -1;
          iter = parent;
          break;
        case 1:
          target = ENVIRONMENTS_ENVIRONMENT_COLUMN;
	  editable = -1;
          iter = child;
          break;
        case 2:
          target = ENVIRONMENTS_GROUP_COLUMN;
          editable = ENVIRONMENTS_GROUP_EDITABLE_COLUMN;
          iter = parent;
          break;
        case 3:
          target = ENVIRONMENTS_GROUP_COLUMN;
          editable = ENVIRONMENTS_GROUP_EDITABLE_COLUMN;
          iter = child;
          break;
        case 4:
          target = ENVIRONMENTS_WIDTH_COLUMN;
          editable = ENVIRONMENTS_WIDTH_EDITABLE_COLUMN;
	  iter = parent;
	  break;
        case 5:
          target = ENVIRONMENTS_HEIGHT_COLUMN;
          editable = ENVIRONMENTS_HEIGHT_EDITABLE_COLUMN;
	  iter = parent;
	  break;
        case 6:
          target = ENVIRONMENTS_WIDTH_COLUMN;
          editable = ENVIRONMENTS_WIDTH_EDITABLE_COLUMN;
	  iter = child;
	  break;
        case 7:
          target = ENVIRONMENTS_HEIGHT_COLUMN;
          editable = ENVIRONMENTS_HEIGHT_EDITABLE_COLUMN;
	  iter = child;
	  break;
        case 8:
          target = ENVIRONMENTS_AREA_COLUMN;
          editable = -1;
	  iter = parent;
	  break;
        case 9:
          target = ENVIRONMENTS_AREA_COLUMN;
          editable = -1;
	  iter = child;
	  break;
        case 10:
          target = ENVIRONMENTS_SAME_FLOOR_CEIL_COLUMN;
          editable = ENVIRONMENTS_SAME_FLOOR_CEIL_ACTIVATABLE_COLUMN;
          iter = child;
	  if (value[0] == '0')
	    value = NULL;
	  break;
        case 11:
          target = ENVIRONMENTS_HEAT_COLUMN;
          editable = -1;
	  iter = parent;
	  break;
        case 12:
          target = ENVIRONMENTS_HEAT_COLUMN;
          editable = -1;
	  iter = child;
	  break;
        default:
          g_assert_not_reached ();
      }

      if (editable == -1)
        gtk_tree_store_set (GTK_TREE_STORE (model), &iter, target, value, -1);
      else
        gtk_tree_store_set (GTK_TREE_STORE (model), &iter, target, value, editable, TRUE, -1);
    }
  }
}

static void
set_enclosures_tree (GtkTreeModel *model, const gchar* query)
{
  GtkTreeIter child;
  GtkTreeIter parent;
  GtkTreeIter iter;
  sqlite3_stmt *statement;
  gchar *value;
  gchar *group = NULL;
  gint column;
  gint target;
  gint editable;

  if (sqlite3_prepare (database_handler, query, -1, &statement, NULL) != SQLITE_OK) {
    gchar *message = g_strdup_printf ("<i>%s</i> al ejecutar el comando <i>%s</i>", sqlite3_errmsg (database_handler), query);
    show_error_dialog ("Error SQL", "Error al ejecutar el comando SQL", message);
    g_free (message);
    return;
  }

  while (sqlite3_step (statement) != SQLITE_DONE) {
    value = (gchar *) sqlite3_column_text (statement, 0);

    /* if are distinct, add new group */
    if (g_strcmp0 (group, value)) {
      gtk_tree_store_append (GTK_TREE_STORE (model), &parent, NULL);

      if (group)
        g_free (group);

      group = g_strdup (value);
    }

    gtk_tree_store_append (GTK_TREE_STORE (model), &child, &parent);

    for (column = 0; column < 8; column++) {
      value = (gchar *) sqlite3_column_text (statement, column);

      /*
       * 00:	cerramiento:	ENCLOSURES_ENCLOSURE_COLUMN:		P
       * 01:	material:	ENCLOSURES_ENCLOSURE_COLUMN:		C
       * 02:	tipo:		ENCLOSURES_TYPE_COLUMN:			P
       * 03:	espesor:	ENCLOSURES_THICKNESS_COLUMN:		P
       * 04:	espesor: 	ENCLOSURES_THICKNESS_COLUMN:		C
       * 05:	transmitancia:	ENCLOSURES_TRANSMITTANCE_COLUMN:	P
       * 06:	conductividad:	ENCLOSURES_TRANSMITTANCE_COLUMN:	C
       * 07:	doble:		ENCLOSURES_BOTH_SIDES_COLUMN:		C
       */

      switch (column) {
        case 0:
          target = ENCLOSURES_ENCLOSURE_COLUMN;
	  editable = -1;
          iter = parent;
          break;
        case 1:
          target = ENCLOSURES_ENCLOSURE_COLUMN;
	  editable = -1;
          iter = child;
          break;
        case 2:
          target = ENCLOSURES_TYPE_COLUMN;
          editable = ENCLOSURES_TYPE_EDITABLE_COLUMN;
          iter = parent;
          break;
        case 3:
          target = ENCLOSURES_THICKNESS_COLUMN;
          editable = -1;
	  iter = parent;
	  break;
        case 4:
          target = ENCLOSURES_THICKNESS_COLUMN;
          editable = ENCLOSURES_THICKNESS_EDITABLE_COLUMN;
	  iter = child;
	  break;
        case 5:
          target = ENCLOSURES_TRANSMITTANCE_COLUMN;
          editable = -1;
	  iter = parent;
	  break;
        case 6:
          target = ENCLOSURES_TRANSMITTANCE_COLUMN;
          editable = -1;
	  iter = child;
	  break;
        case 7:
          target = ENCLOSURES_BOTH_SIDES_COLUMN;
          editable = ENCLOSURES_BOTH_SIDES_ACTIVATABLE_COLUMN;
          iter = child;
	  if (value[0] == '0')
	    value = NULL;
	  break;
        default:
          g_assert_not_reached ();
      }

      if (editable == -1)
        gtk_tree_store_set (GTK_TREE_STORE (model), &iter, target, value, -1);
      else
        gtk_tree_store_set (GTK_TREE_STORE (model), &iter, target, value, editable, TRUE, -1);
    }
  }
}

static GtkTreeModel *
create_environments_tree_model (void)
{
  GtkTreeStore *model;

  /* store */
  model = gtk_tree_store_new (ENVIRONMENTS_COLUMNS, G_TYPE_STRING,
						    G_TYPE_STRING,
						    G_TYPE_STRING,
						    G_TYPE_STRING,
						    G_TYPE_STRING,
						    G_TYPE_STRING,
						    G_TYPE_BOOLEAN,
						    G_TYPE_INT,
						    G_TYPE_BOOLEAN,
						    G_TYPE_BOOLEAN,
						    G_TYPE_BOOLEAN,
						    G_TYPE_BOOLEAN,
						    G_TYPE_BOOLEAN,
						    G_TYPE_BOOLEAN,
						    G_TYPE_BOOLEAN,
						    G_TYPE_BOOLEAN,
						    G_TYPE_BOOLEAN);

  return (GTK_TREE_MODEL (model));
}

static GtkTreeModel *
create_enclosures_tree_model (void)
{
  GtkTreeStore *model;

  /* store */
  model = gtk_tree_store_new (ENCLOSURES_COLUMNS, G_TYPE_STRING,
  						  G_TYPE_STRING,
  						  G_TYPE_STRING,
  						  G_TYPE_STRING,
						  G_TYPE_BOOLEAN,
						  G_TYPE_BOOLEAN,
						  G_TYPE_BOOLEAN,
						  G_TYPE_BOOLEAN,
						  G_TYPE_BOOLEAN);

  return (GTK_TREE_MODEL (model));
}

static GtkTreeModel *
create_enclosures_list_model (void)
{
  GtkListStore *model;

  /* store */
  model = gtk_list_store_new (1, G_TYPE_STRING);

  return (GTK_TREE_MODEL (model));
}

static GtkTreeModel *
create_materials_list_model (void)
{
  GtkListStore *model;

  /* store */
  model = gtk_list_store_new (MATERIALS_COLUMNS, G_TYPE_STRING,
  						 G_TYPE_STRING,
						 G_TYPE_STRING);

  return (GTK_TREE_MODEL (model));
}

static GtkTreeModel *
create_locations_list_model (void)
{
  GtkListStore *model;

  /* store */
  model = gtk_list_store_new (MATERIALS_COLUMNS, G_TYPE_STRING,
  						 G_TYPE_STRING,
						 G_TYPE_STRING);

  return (GTK_TREE_MODEL (model));
}

static GtkTreeModel *
create_builds_list_model (void)
{
  GtkListStore *model;

  /* store */
  model = gtk_list_store_new (1, G_TYPE_STRING);

  return (GTK_TREE_MODEL (model));
}

static GtkTreeModel *
create_enclosure_types_list_model (void)
{
  GtkListStore *model;

  /* store */
  model = gtk_list_store_new (1, G_TYPE_STRING);

  return (GTK_TREE_MODEL (model));
}

static GtkTreeModel *
create_environment_groups_list_model (void)
{
  GtkListStore *model;

  /* store */
  model = gtk_list_store_new (1, G_TYPE_STRING);

  return (GTK_TREE_MODEL (model));
}

static void
environment_editing_started (GtkCellRenderer *cell, GtkCellEditable *editable, const gchar *path, gpointer data)
{
  if (GTK_IS_ENTRY (editable) && !is_parent (path)) {
    GtkEntry *entry = GTK_ENTRY (editable);
    GtkTreeModel *environments_model = gtk_tree_view_get_model (GTK_TREE_VIEW (data));
    GtkEntryCompletion *completion = gtk_entry_completion_new ();
    GtkTreeModel *enclosures_model = create_enclosures_list_model ();
    GtkTreeIter iter;
    gboolean is_horizontal;
    gboolean is_vertical;
    gboolean is_window;
    gchar *type;
    gchar *query;

    gtk_tree_model_get_iter (environments_model, &iter, gtk_tree_path_new_from_string (path));

    gtk_tree_model_get (environments_model, &iter, ENVIRONMENTS_IS_HORIZONTAL_COLUMN, &is_horizontal,
                                    		   ENVIRONMENTS_IS_VERTICAL_COLUMN, &is_vertical,
                                      		   ENVIRONMENTS_IS_WINDOW_COLUMN, &is_window, -1);

    if (is_window)
      type = g_strdup ("Ventana");
    else if (is_horizontal)
      type = g_strdup ("Horizontal");
    else if (is_vertical)
      type = g_strdup ("Vertical");
    else
      type = NULL;

    if (type)
      query = g_strdup_printf ("select cerramiento from cerramientos where tipo = '%s' order by cerramiento;", type);
    else
      query = g_strdup ("select cerramiento from cerramientos;");

    set_data_list (enclosures_model, query);
    g_free (query);
    g_free (type);

    gtk_entry_completion_set_model (completion, enclosures_model);
    g_object_unref (enclosures_model);

    /* TODO Implementar ellipsize */
    gtk_entry_completion_set_text_column (completion, 0);
    gtk_entry_completion_set_inline_completion (completion, TRUE);
    gtk_entry_completion_set_inline_selection (completion, TRUE);
    gtk_entry_completion_set_popup_completion (completion, TRUE);
    gtk_entry_completion_set_popup_set_width (completion, TRUE);
    gtk_entry_completion_set_popup_single_match (completion, TRUE);
    gtk_entry_completion_set_minimum_key_length (completion, 0);
      
    gtk_entry_set_completion (entry, completion);
  }
}

static void
enclosure_editing_started (GtkCellRenderer *cell, GtkCellEditable *editable, const gchar *path, gpointer data)
{
  if (GTK_IS_ENTRY (editable) && !is_parent (path)) {
    GtkEntry *entry = GTK_ENTRY (editable);
    GtkEntryCompletion *completion = gtk_entry_completion_new ();
    GtkTreeModel *model = create_materials_list_model ();

    set_data_list (model, "select material || case when densidad is not null then ' de ' || densidad || ' kg/m³' else '' end as material "
      "from materiales order by material;");

    gtk_entry_completion_set_model (completion, model);
    g_object_unref (model);

    /* TODO Implementar ellipsize */
    gtk_entry_completion_set_text_column (completion, 0);
    gtk_entry_completion_set_inline_completion (completion, TRUE);
    gtk_entry_completion_set_inline_selection (completion, TRUE);
    gtk_entry_completion_set_popup_completion (completion, TRUE);
    gtk_entry_completion_set_popup_set_width (completion, TRUE);
    gtk_entry_completion_set_popup_single_match (completion, TRUE);
    gtk_entry_completion_set_minimum_key_length (completion, 0);
      
    gtk_entry_set_completion (entry, completion);
  }
}

static void
material_edited (GtkCellRendererText *renderer, const gchar *path, const gchar *value, gpointer data)
{
  GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (data));
  GtkTreeIter iter;

  gtk_tree_model_get_iter (model, &iter, gtk_tree_path_new_from_string (path));
  gtk_list_store_set (GTK_LIST_STORE (model), &iter, MATERIALS_MATERIAL_COLUMN, value, -1);
}

static void
density_edited (GtkCellRendererText *renderer, const gchar *path, const gchar *value, gpointer data)
{
  GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (data));
  GtkTreeIter iter;

  gtk_tree_model_get_iter (model, &iter, gtk_tree_path_new_from_string (path));
  gtk_list_store_set (GTK_LIST_STORE (model), &iter, MATERIALS_DENSITY_COLUMN, value, -1);
}

static void
conductivity_edited (GtkCellRendererText *renderer, const gchar *path, const gchar *value, gpointer data)
{
  GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (data));
  GtkTreeIter iter;

  gtk_tree_model_get_iter (model, &iter, gtk_tree_path_new_from_string (path));
  gtk_list_store_set (GTK_LIST_STORE (model), &iter, MATERIALS_CONDUCTIVITY_COLUMN, value, -1);
}

static void
enclosure_edited (GtkCellRendererText *renderer, const gchar *path, const gchar *value, gpointer data)
{
  GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (data));
  GtkTreeIter iter;

  gtk_tree_model_get_iter (model, &iter, gtk_tree_path_new_from_string (path));
  gtk_tree_store_set (GTK_TREE_STORE (model), &iter, ENCLOSURES_ENCLOSURE_COLUMN, value, -1);

  /*
   * GError *error = NULL;
   * GRegex *expression;
   * GMatchInfo *matches;
   * gchar *material;
   * gchar *density;
   * 
   * expression = g_regex_new ("^(.+) de ([0-9]+) kg/m³$", 0, 0, &error);
   *
   * if (error)
   *   show_error_dialog ("Error interno", "Error al compilar una expresión regular", error->message);
   * else
   *   if (g_regex_match (expression, value, 0, &matches)) {
   *     material = g_match_info_fetch (matches, 1);
   *     density = g_match_info_fetch (matches, 2);
   *
   *     g_print ("Material: %s\n", material);
   *     g_print ("Densidad: %s\n", density);
   * 
   *     g_free (material);
   *     g_free (density);
   *     g_match_info_matches (matches);
   *   }
   * 
   * g_regex_unref (expression);
   */
}

static void
enclosures_type_edited (GtkCellRendererText *renderer, const gchar *path, const gchar *value, gpointer data)
{
  GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (data));
  GtkTreeIter iter;

  gtk_tree_model_get_iter (model, &iter, gtk_tree_path_new_from_string (path));
  gtk_tree_store_set (GTK_TREE_STORE (model), &iter, ENCLOSURES_TYPE_COLUMN, value, -1);
}

static void
thickness_edited (GtkCellRendererText *renderer, const gchar *path, const gchar *value, gpointer data)
{
  GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (data));
  GtkTreeIter iter;

  gtk_tree_model_get_iter (model, &iter, gtk_tree_path_new_from_string (path));
  gtk_tree_store_set (GTK_TREE_STORE (model), &iter, ENCLOSURES_THICKNESS_COLUMN, value, -1);
}

/* only on parents without childrens */
static void
transmittance_edited (GtkCellRendererText *renderer, const gchar *path, const gchar *value, gpointer data)
{
  GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (data));
  GtkTreeIter iter;

  gtk_tree_model_get_iter (model, &iter, gtk_tree_path_new_from_string (path));
  gtk_tree_store_set (GTK_TREE_STORE (model), &iter, ENCLOSURES_TRANSMITTANCE_COLUMN, value, -1);
}

static void
both_sides_toggled (GtkCellRendererText *renderer, const gchar *path, gpointer data)
{
  GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (data));
  GtkTreeIter iter;
  gboolean value;

  gtk_tree_model_get_iter (model, &iter, gtk_tree_path_new_from_string (path));

  gtk_tree_model_get (model, &iter, ENCLOSURES_BOTH_SIDES_COLUMN, &value, -1);

  value ^= 1;

  gtk_tree_store_set (GTK_TREE_STORE (model), &iter, ENCLOSURES_BOTH_SIDES_COLUMN, value, -1);
}

static void
environment_edited (GtkCellRendererText *renderer, const gchar *path, const gchar *value, gpointer data)
{
  GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (data));
  GtkTreeIter iter;

  gtk_tree_model_get_iter (model, &iter, gtk_tree_path_new_from_string (path));
  gtk_tree_store_set (GTK_TREE_STORE (model), &iter, ENVIRONMENTS_ENVIRONMENT_COLUMN, value, -1);
}

static void
environments_group_edited (GtkCellRendererText *renderer, const gchar *path, const gchar *value, gpointer data)
{
  GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (data));
  GtkTreeIter iter;

  gtk_tree_model_get_iter (model, &iter, gtk_tree_path_new_from_string (path));
  gtk_tree_store_set (GTK_TREE_STORE (model), &iter, ENVIRONMENTS_GROUP_COLUMN, value, -1);
}

static void
width_edited (GtkCellRendererText *renderer, const gchar *path, const gchar *value, gpointer data)
{
  GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (data));
  GtkTreeIter iter;

  gtk_tree_model_get_iter (model, &iter, gtk_tree_path_new_from_string (path));
  gtk_tree_store_set (GTK_TREE_STORE (model), &iter, ENVIRONMENTS_WIDTH_COLUMN, value, -1);

  if (!is_parent (path)) {
    sqlite3_stmt *statement;
    gdouble width = atof (value);
    gdouble height;
    gdouble area;
    gchar *buffer;

    gtk_tree_model_get (model, &iter, ENVIRONMENTS_HEIGHT_COLUMN, &buffer, -1);

    if (buffer)
      height = atof (buffer);

    area = width * height;

    buffer = g_strdup_printf ("%0.02f", area);

    gtk_tree_store_set (GTK_TREE_STORE (model), &iter, ENVIRONMENTS_AREA_COLUMN, buffer, -1);

    g_free (buffer);
  }
}

static void
height_edited (GtkCellRendererText *renderer, const gchar *path, const gchar *value, gpointer data)
{
  GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (data));
  GtkTreeIter iter;

  gtk_tree_model_get_iter (model, &iter, gtk_tree_path_new_from_string (path));
  gtk_tree_store_set (GTK_TREE_STORE (model), &iter, ENVIRONMENTS_HEIGHT_COLUMN, value, -1);

  if (!is_parent (path)) {
    sqlite3_stmt *statement;
    gdouble height = atof (value);
    gdouble width;
    gdouble area;
    gchar *buffer;

    gtk_tree_model_get (model, &iter, ENVIRONMENTS_WIDTH_COLUMN, &buffer, -1);

    if (buffer)
      width = atof (buffer);

    area = width * height;

    buffer = g_strdup_printf ("%0.02f", area);

    gtk_tree_store_set (GTK_TREE_STORE (model), &iter, ENVIRONMENTS_AREA_COLUMN, buffer, -1);

    g_free (buffer);
  }
}

static void
same_floor_ceil_toggled (GtkCellRendererText *renderer, const gchar *path, gpointer data)
{
  GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (data));
  GtkTreeIter iter;
  gboolean value;

  gtk_tree_model_get_iter (model, &iter, gtk_tree_path_new_from_string (path));

  gtk_tree_model_get (model, &iter, ENVIRONMENTS_SAME_FLOOR_CEIL_COLUMN, &value, -1);

  value ^= 1;

  gtk_tree_store_set (GTK_TREE_STORE (model), &iter, ENVIRONMENTS_SAME_FLOOR_CEIL_COLUMN, value, -1);
}

static void
location_edited (GtkCellRendererText *renderer, const gchar *path, const gchar *value, gpointer data)
{
  GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (data));
  GtkTreeIter iter;

  gtk_tree_model_get_iter (model, &iter, gtk_tree_path_new_from_string (path));
  gtk_list_store_set (GTK_LIST_STORE (model), &iter, LOCATIONS_LOCATION_COLUMN, value, -1);
}

static void
minimum_edited (GtkCellRendererText *renderer, const gchar *path, const gchar *value, gpointer data)
{
  GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (data));
  GtkTreeIter iter;

  gtk_tree_model_get_iter (model, &iter, gtk_tree_path_new_from_string (path));
  gtk_list_store_set (GTK_LIST_STORE (model), &iter, LOCATIONS_MINIMUM_COLUMN, value, -1);
}

static void
maximum_edited (GtkCellRendererText *renderer, const gchar *path, const gchar *value, gpointer data)
{
  GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (data));
  GtkTreeIter iter;

  gtk_tree_model_get_iter (model, &iter, gtk_tree_path_new_from_string (path));
  gtk_list_store_set (GTK_LIST_STORE (model), &iter, LOCATIONS_MAXIMUM_COLUMN, value, -1);
}

gboolean
query_enclosure_type_tooltip (GtkWidget *widget, gint x, gint y, gboolean keyboard_mode, GtkTooltip *tooltip, gpointer data)
{
  GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (data);
  GtkTreeView *treeview = GTK_TREE_VIEW (gtk_tree_view_column_get_tree_view (column));

  gtk_tooltip_set_markup (tooltip, "El <b>tipo de cerramiento</b> puede ser <i>medianero al exterior</i> cuando limita al exterior "
				   "(calle, patio, pasillo, fondo); <i>medianero a una obra</i> cuando limita a una obra no calefaccionada "
				   "o <i>medianero interior</i> cuando el cerramiento divide dos ambientes calefaccionados.");
  gtk_tooltip_set_icon_from_stock (tooltip, GTK_STOCK_HELP, GTK_ICON_SIZE_MENU);
  gtk_tree_view_set_tooltip_cell (treeview, tooltip, /* path */ NULL, column, /* renderer */ NULL);

  return (TRUE);
}

gboolean
query_enclosure_both_sides_tooltip (GtkWidget *widget, gint x, gint y, gboolean keyboard_mode, GtkTooltip *tooltip, gpointer data)
{
  GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (data);
  GtkTreeView *treeview = GTK_TREE_VIEW (gtk_tree_view_column_get_tree_view (column));

  gtk_tooltip_set_markup (tooltip, "La columna <b>doble capa</b> indica que el material está a ambos lados del muro. Úselo, por ejemplo, "
				   "cuando una pared tiene el <i>mismo revoque en ambos lados</i> o cuando haya que <i>multiplicar el "
				   "espesor ×2</i>.");
  gtk_tooltip_set_icon_from_stock (tooltip, GTK_STOCK_HELP, GTK_ICON_SIZE_MENU);
  gtk_tree_view_set_tooltip_cell (treeview, tooltip, /* path */ NULL, column, /* renderer */ NULL);

  return (TRUE);
}

gboolean
query_environment_same_floor_ceil_tooltip (GtkWidget *widget, gint x, gint y, gboolean keyboard_mode, GtkTooltip *tooltip, gpointer data)
{
  GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (data);
  GtkTreeView *treeview = GTK_TREE_VIEW (gtk_tree_view_column_get_tree_view (column));

  gtk_tooltip_set_markup (tooltip, "La columna <b>igual techo a piso</b> indica que los valores de transmitancia, tipo y medidas del techo "
  				   "son <i>iguales al piso</i>. Utilícelo para cálculo de balance térmico de departamentos.");
  gtk_tooltip_set_icon_from_stock (tooltip, GTK_STOCK_HELP, GTK_ICON_SIZE_MENU);
  gtk_tree_view_set_tooltip_cell (treeview, tooltip, /* path */ NULL, column, /* renderer */ NULL);

  return (TRUE);
}

gboolean
query_height_tooltip (GtkWidget *widget, gint x, gint y, gboolean keyboard_mode, GtkTooltip *tooltip, gpointer data)
{
  gtk_tooltip_set_markup (tooltip, "La <b>altura promedio</b> es la altura que se usará para cada cerramiento en todos los ambientes. "
				   "Si la obra no tiene una <i>única altura</i>, por ejemplo, una vivienda con techo inclinado, <i>deje "
				   "este valor en cero</i>.");
  gtk_tooltip_set_icon_from_stock (tooltip, GTK_STOCK_HELP, GTK_ICON_SIZE_MENU);

  return (TRUE);
}

gboolean
query_same_floor_ceil_tooltip (GtkWidget *widget, gint x, gint y, gboolean keyboard_mode, GtkTooltip *tooltip, gpointer data)
{
  gtk_tooltip_set_markup (tooltip, "Si marca <b>igualar piso y techo en todos los ambientes</b> el valor de transmitancia del piso y techo "
				   "el primer ambiente que defina se utilizará en el resto de los ambientes que vaya agregando después. "
				   "Si los <i>coeficientes de transmitancia o son iguales</i> en todos los pisos y techos de cada ambiente, "
				   "<i>deje la casilla desmarcada</i>.");
  gtk_tooltip_set_icon_from_stock (tooltip, GTK_STOCK_HELP, GTK_ICON_SIZE_MENU);

  return (TRUE);
}

static void
add_environments_columns (GtkTreeView *treeview)
{
  GtkCellRenderer *renderer;
  GtkTreeViewColumn *column;

  /* column */
  renderer = gtk_cell_renderer_text_new ();

  g_object_set (renderer,
  		"editable", TRUE,
  		"ellipsize", PANGO_ELLIPSIZE_END,
  		"width-chars", 35,
		NULL);

  g_signal_connect (renderer, "edited", G_CALLBACK (environment_edited), treeview);
  g_signal_connect (renderer, "editing-started", G_CALLBACK (environment_editing_started), treeview);

  column = gtk_tree_view_column_new_with_attributes ("Cerramiento", renderer, "text", ENVIRONMENTS_ENVIRONMENT_COLUMN, NULL);
  gtk_tree_view_column_set_sort_column_id (column, ENVIRONMENTS_ENVIRONMENT_COLUMN);
  gtk_tree_view_column_set_resizable (column, TRUE);
  gtk_tree_view_append_column (treeview, column);

  /* column */
  renderer = gtk_cell_renderer_combo_new ();
  GtkTreeModel *model = create_environment_groups_list_model ();
  set_data_list (model, "select grupo from grupos order by grupo;");

  g_object_set (renderer,
  		"model", model,
  		"text-column", 0,
  		"has-entry", FALSE,
		NULL);

  g_signal_connect (renderer, "edited", G_CALLBACK (environments_group_edited), treeview);

  g_object_unref (model);

  column = gtk_tree_view_column_new_with_attributes ("Tipo", renderer, "markup", ENVIRONMENTS_GROUP_COLUMN,
  						     "editable", ENVIRONMENTS_GROUP_EDITABLE_COLUMN, NULL);
  gtk_tree_view_column_set_resizable (column, TRUE);
  gtk_tree_view_append_column (treeview, column);

  g_signal_connect (treeview, "query-tooltip", G_CALLBACK (query_enclosure_type_tooltip), column);

  /* column */
  renderer = gtk_cell_renderer_text_new ();
  g_signal_connect (renderer, "edited", G_CALLBACK (width_edited), treeview);
  column = gtk_tree_view_column_new_with_attributes ("A (m)", renderer, "text", ENVIRONMENTS_WIDTH_COLUMN,
  						     "editable", ENVIRONMENTS_WIDTH_EDITABLE_COLUMN, NULL);
  gtk_tree_view_column_set_resizable (column, TRUE);
  gtk_tree_view_append_column (treeview, column);

  /* column */
  renderer = gtk_cell_renderer_text_new ();
  g_signal_connect (renderer, "edited", G_CALLBACK (height_edited), treeview);
  column = gtk_tree_view_column_new_with_attributes ("B (m)", renderer, "text", ENVIRONMENTS_HEIGHT_COLUMN,
  						     "editable", ENVIRONMENTS_HEIGHT_EDITABLE_COLUMN, NULL);
  gtk_tree_view_column_set_resizable (column, TRUE);
  gtk_tree_view_append_column (treeview, column);

  /* column */
  renderer = gtk_cell_renderer_toggle_new ();
  g_signal_connect (renderer, "toggled", G_CALLBACK (same_floor_ceil_toggled), treeview);
  column = gtk_tree_view_column_new_with_attributes ("Igualar al piso",
  						     renderer, "active", ENVIRONMENTS_SAME_FLOOR_CEIL_COLUMN,
  						     "activatable", ENVIRONMENTS_SAME_FLOOR_CEIL_ACTIVATABLE_COLUMN, NULL);
  gtk_tree_view_append_column (treeview, column);

  g_signal_connect (treeview, "query-tooltip", G_CALLBACK (query_environment_same_floor_ceil_tooltip), column);

  /* column */
  renderer = gtk_cell_renderer_text_new ();
  column = gtk_tree_view_column_new_with_attributes ("Superficie (m²)", renderer, "markup", ENVIRONMENTS_AREA_COLUMN, NULL);
  gtk_tree_view_column_set_resizable (column, TRUE);
  gtk_tree_view_append_column (treeview, column);

  /* column */
  renderer = gtk_cell_renderer_text_new ();
  column = gtk_tree_view_column_new_with_attributes ("Calor (W)", renderer, "markup", ENVIRONMENTS_HEAT_COLUMN, NULL);
  gtk_tree_view_column_set_resizable (column, TRUE);
  gtk_tree_view_append_column (treeview, column);
}

static void
add_enclosures_columns (GtkTreeView *treeview)
{
  GtkCellRenderer *renderer;
  GtkTreeViewColumn *column;

  /* column */
  renderer = gtk_cell_renderer_text_new ();

  g_object_set (renderer,
  		"editable", TRUE,
  		"ellipsize", PANGO_ELLIPSIZE_END,
  		"width-chars", 55,
		NULL);

  g_signal_connect (renderer, "edited", G_CALLBACK (enclosure_edited), treeview);
  g_signal_connect (renderer, "editing-started", G_CALLBACK (enclosure_editing_started), treeview);

  column = gtk_tree_view_column_new_with_attributes ("Material", renderer, "text", ENCLOSURES_ENCLOSURE_COLUMN, NULL);
  gtk_tree_view_column_set_sort_column_id (column, ENCLOSURES_ENCLOSURE_COLUMN);
  gtk_tree_view_column_set_resizable (column, TRUE);
  gtk_tree_view_append_column (treeview, column);

  /* column */
  renderer = gtk_cell_renderer_combo_new ();
  GtkTreeModel *model = create_enclosure_types_list_model ();
  set_data_list (model, "select tipo from tipos order by tipo;");

  g_object_set (renderer,
  		"model", model,
  		"text-column", 0,
  		"has-entry", FALSE,
		NULL);

  g_signal_connect (renderer, "edited", G_CALLBACK (enclosures_type_edited), treeview);

  g_object_unref (model);

  column = gtk_tree_view_column_new_with_attributes ("Tipo", renderer, "text", ENCLOSURES_TYPE_COLUMN,
  						     "editable", ENCLOSURES_TYPE_EDITABLE_COLUMN, NULL);
  gtk_tree_view_column_set_resizable (column, TRUE);
  gtk_tree_view_append_column (treeview, column);

  /* column */
  renderer = gtk_cell_renderer_text_new ();
  g_signal_connect (renderer, "edited", G_CALLBACK (thickness_edited), treeview);
  column = gtk_tree_view_column_new_with_attributes ("Espesor (cm)", renderer, "markup", ENCLOSURES_THICKNESS_COLUMN,
  						     "editable", ENCLOSURES_THICKNESS_EDITABLE_COLUMN, NULL);
  gtk_tree_view_column_set_resizable (column, TRUE);
  gtk_tree_view_append_column (treeview, column);

  /* column */
  renderer = gtk_cell_renderer_text_new ();
  g_signal_connect (renderer, "edited", G_CALLBACK (transmittance_edited), treeview);
  column = gtk_tree_view_column_new_with_attributes ("Transmitancia K (W/m²°C)",
  						     renderer, "markup", ENCLOSURES_TRANSMITTANCE_COLUMN,
  						     "editable", ENCLOSURES_TRANSMITTANCE_EDITABLE_COLUMN, NULL);
  gtk_tree_view_column_set_resizable (column, TRUE);
  gtk_tree_view_append_column (treeview, column);

  /* column */
  renderer = gtk_cell_renderer_toggle_new ();
  g_signal_connect (renderer, "toggled", G_CALLBACK (both_sides_toggled), treeview);
  column = gtk_tree_view_column_new_with_attributes ("Doble",
  						     renderer, "active", ENCLOSURES_BOTH_SIDES_COLUMN,
  						     "activatable", ENCLOSURES_BOTH_SIDES_ACTIVATABLE_COLUMN, NULL);
  gtk_tree_view_append_column (treeview, column);

  g_signal_connect (treeview, "query-tooltip", G_CALLBACK (query_enclosure_both_sides_tooltip), column);
}

static void
add_materials_columns (GtkTreeView *treeview)
{
  GtkCellRenderer *renderer;
  GtkTreeViewColumn *column;

  /* column */
  renderer = gtk_cell_renderer_text_new ();

  g_object_set (renderer,
  		"editable", TRUE,
  		"ellipsize", PANGO_ELLIPSIZE_END,
  		"width-chars", 55,
		NULL);

  g_signal_connect (renderer, "edited", G_CALLBACK (material_edited), treeview);

  column = gtk_tree_view_column_new_with_attributes ("Material", renderer, "text", MATERIALS_MATERIAL_COLUMN, NULL);
  gtk_tree_view_column_set_sort_column_id (column, MATERIALS_MATERIAL_COLUMN);
  gtk_tree_view_column_set_resizable (column, TRUE);
  gtk_tree_view_append_column (treeview, column);

  /* column */
  renderer = gtk_cell_renderer_text_new ();
  g_object_set (renderer, "editable", TRUE, NULL);
  g_signal_connect (renderer, "edited", G_CALLBACK (density_edited), treeview);
  column = gtk_tree_view_column_new_with_attributes ("Densidad (kg/m³)", renderer, "text", MATERIALS_DENSITY_COLUMN, NULL);
  gtk_tree_view_column_set_resizable (column, TRUE);
  gtk_tree_view_append_column (treeview, column);

  /* column */
  renderer = gtk_cell_renderer_text_new ();
  g_object_set (renderer, "editable", TRUE, NULL);
  g_signal_connect (renderer, "edited", G_CALLBACK (conductivity_edited), treeview);
  column = gtk_tree_view_column_new_with_attributes ("Conductividad ? (W/m°C)", renderer, "text", MATERIALS_CONDUCTIVITY_COLUMN, NULL);
  gtk_tree_view_column_set_resizable (column, TRUE);
  gtk_tree_view_append_column (treeview, column);
}

static void
add_locations_columns (GtkTreeView *treeview)
{
  GtkCellRenderer *renderer;
  GtkTreeViewColumn *column;

  /* column */
  renderer = gtk_cell_renderer_text_new ();

  g_object_set (renderer,
  		"editable", TRUE,
  		"ellipsize", PANGO_ELLIPSIZE_END,
  		"width-chars", 60,
		NULL);

  g_signal_connect (renderer, "edited", G_CALLBACK (location_edited), treeview);
  column = gtk_tree_view_column_new_with_attributes ("Ciudad", renderer, "text", LOCATIONS_LOCATION_COLUMN, NULL);
  gtk_tree_view_column_set_sort_column_id (column, LOCATIONS_LOCATION_COLUMN);
  gtk_tree_view_column_set_resizable (column, TRUE);
  gtk_tree_view_append_column (treeview, column);

  /* column */
  renderer = gtk_cell_renderer_text_new ();
  g_object_set (renderer, "editable", TRUE, NULL);
  g_signal_connect (renderer, "edited", G_CALLBACK (minimum_edited), treeview);
  column = gtk_tree_view_column_new_with_attributes ("Mínima (°C)", renderer, "text", LOCATIONS_MINIMUM_COLUMN, NULL);
  gtk_tree_view_column_set_resizable (column, TRUE);
  gtk_tree_view_append_column (treeview, column);

  /* column */
  renderer = gtk_cell_renderer_text_new ();
  g_object_set (renderer, "editable", TRUE, NULL);
  g_signal_connect (renderer, "edited", G_CALLBACK (maximum_edited), treeview);
  column = gtk_tree_view_column_new_with_attributes ("Máxima (°C)", renderer, "text", LOCATIONS_MAXIMUM_COLUMN, NULL);
  gtk_tree_view_column_set_resizable (column, TRUE);
  gtk_tree_view_append_column (treeview, column);
}

void
open_database (const gchar *database)
{
  if (sqlite3_open (database, &database_handler) != SQLITE_OK)
    show_error_dialog ("Error SQL", "Error al abrir la base de datos ", sqlite3_errmsg (database_handler));
}

void
close_database ()
{
  sqlite3_close (database_handler);
}

gboolean
main_window_delete_event (GtkWidget *widget, GdkEvent *event, gpointer data)  
{
  close_database ();
  gtk_main_quit ();
}

void
save_clicked (GtkWidget *widget, gpointer data)
{
  /* TODO */
}

void
print_clicked (GtkWidget *widget, gpointer data)
{
  /* TODO */
}

void
new_material_clicked (GtkWidget *widget, gpointer data)
{
  GtkBuilder *builder = GTK_BUILDER (data);
  GtkTreeView *treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder, "materials_treeview"));
  GtkWidget *remove_material_button = GTK_WIDGET (gtk_builder_get_object (builder, "remove_material_button"));
  GtkTreeModel *model = gtk_tree_view_get_model (treeview);
  GtkTreeViewColumn *column;
  GtkTreePath *path;
  GtkTreeIter iter;

  gtk_list_store_append (GTK_LIST_STORE (model), &iter);
  gtk_list_store_set (GTK_LIST_STORE (model), &iter, MATERIALS_MATERIAL_COLUMN, "Nuevo material", -1);

  path = gtk_tree_model_get_path (model, &iter);
  column = gtk_tree_view_get_column (treeview, MATERIALS_MATERIAL_COLUMN);
  gtk_tree_view_set_cursor (treeview, path, column, TRUE);

  gtk_tree_path_free (path);

  gtk_widget_set_sensitive (remove_material_button, TRUE);
}

void
new_location_clicked (GtkWidget *widget, gpointer data)
{
  GtkBuilder *builder = GTK_BUILDER (data);
  GtkTreeView *treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder, "locations_treeview"));
  GtkWidget *remove_location_button = GTK_WIDGET (gtk_builder_get_object (builder, "remove_location_button"));
  GtkTreeModel *model = gtk_tree_view_get_model (treeview);
  GtkTreeViewColumn *column;
  GtkTreePath *path;
  GtkTreeIter iter;

  gtk_list_store_append (GTK_LIST_STORE (model), &iter);
  gtk_list_store_set (GTK_LIST_STORE (model), &iter, LOCATIONS_LOCATION_COLUMN, "Nueva ciudad", -1);

  path = gtk_tree_model_get_path (model, &iter);
  column = gtk_tree_view_get_column (treeview, LOCATIONS_LOCATION_COLUMN);
  gtk_tree_view_set_cursor (treeview, path, column, TRUE);

  gtk_tree_path_free (path);

  gtk_widget_set_sensitive (remove_location_button, TRUE);
}

void
delete_window_clicked (GtkWidget *widget, gpointer data)
{
  GtkBuilder *builder = GTK_BUILDER (data);
  GtkTreeView *treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder, "environments_treeview"));
  GtkWidget *remove_environment_button = GTK_WIDGET (gtk_builder_get_object (builder, "remove_environment_button"));
  GtkWidget *add_enclosure_button = GTK_WIDGET (gtk_builder_get_object (builder, "add_enclosure_button"));
  GtkWidget *delete_enclosure_button = GTK_WIDGET (gtk_builder_get_object (builder, "delete_enclosure_button"));
  GtkWidget *add_window_button = GTK_WIDGET (gtk_builder_get_object (builder, "add_window_button"));
  GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
  GtkTreeModel *model = gtk_tree_view_get_model (treeview);
  GtkTreeIter selected;
  GtkTreeIter parent;
  gint windows;

  if (!gtk_tree_selection_get_selected (selection, NULL, &selected))
    return;

  gtk_tree_model_iter_parent (model, &parent, &selected);

  gtk_tree_model_get (model, &parent, ENVIRONMENTS_WINDOWS_COLUMN, &windows, -1);
  windows--;
  gtk_tree_store_set (GTK_TREE_STORE (model), &parent, ENVIRONMENTS_WINDOWS_COLUMN, windows, -1);

  gtk_tree_store_remove (GTK_TREE_STORE (model), &selected);

  gtk_widget_set_sensitive (remove_environment_button, FALSE);
  gtk_widget_set_sensitive (add_enclosure_button, FALSE);
  gtk_widget_set_sensitive (delete_enclosure_button, FALSE);
  gtk_widget_set_sensitive (add_window_button, FALSE);
  gtk_widget_set_sensitive (widget, FALSE);
}

void
add_window_clicked (GtkWidget *widget, gpointer data)
{
  GtkBuilder *builder = GTK_BUILDER (data);
  GtkTreeView *treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder, "environments_treeview"));
  GtkWidget *delete_window_button = GTK_WIDGET (gtk_builder_get_object (builder, "delete_window_button"));
  GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
  GtkTreeModel *model = gtk_tree_view_get_model (treeview);
  GtkTreeViewColumn *column;
  GtkTreePath *path;
  GtkTreeIter selected;
  GtkTreeIter parent;
  GtkTreeIter iter;
  gint windows;
  gboolean is_window;

  if (!gtk_tree_selection_get_selected (selection, NULL, &selected))
    return;

  gtk_tree_model_get (model, &selected, ENVIRONMENTS_IS_WINDOW_COLUMN, &is_window, -1);

  if (is_window)
    gtk_tree_model_iter_parent (model, &parent, &selected);
  else
    parent = selected;

  gtk_tree_model_get (model, &parent, ENVIRONMENTS_WINDOWS_COLUMN, &windows, -1);
  windows++;

  gtk_tree_store_set (GTK_TREE_STORE (model), &parent, ENVIRONMENTS_WINDOWS_COLUMN, windows, -1);

  gtk_tree_store_append (GTK_TREE_STORE (model), &iter, &parent);
  gtk_tree_store_set (GTK_TREE_STORE (model), &iter, ENVIRONMENTS_ENVIRONMENT_COLUMN, "Nueva ventana",
 		      ENVIRONMENTS_WIDTH_EDITABLE_COLUMN, TRUE,
		      ENVIRONMENTS_HEIGHT_EDITABLE_COLUMN, TRUE,
  		      ENVIRONMENTS_IS_WINDOW_COLUMN, TRUE, -1);

  path = gtk_tree_model_get_path (model, &parent);
  gtk_tree_view_expand_to_path (treeview, path);

  path = gtk_tree_model_get_path (model, &iter);
  column = gtk_tree_view_get_column (treeview, ENVIRONMENTS_ENVIRONMENT_COLUMN);
  gtk_tree_view_set_cursor (treeview, path, column, TRUE);

  gtk_tree_path_free (path);

  gtk_widget_set_sensitive (delete_window_button, TRUE);
}

void
delete_enclosure_clicked (GtkWidget *widget, gpointer data)
{
  GtkBuilder *builder = GTK_BUILDER (data);
  GtkTreeView *treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder, "environments_treeview"));
  GtkWidget *remove_environment_button = GTK_WIDGET (gtk_builder_get_object (builder, "remove_environment_button"));
  GtkWidget *add_enclosure_button = GTK_WIDGET (gtk_builder_get_object (builder, "add_enclosure_button"));
  GtkWidget *add_window_button = GTK_WIDGET (gtk_builder_get_object (builder, "add_window_button"));
  GtkWidget *delete_window_button = GTK_WIDGET (gtk_builder_get_object (builder, "delete_window_button"));
  GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
  GtkTreeModel *model = gtk_tree_view_get_model (treeview);
  GtkTreeIter selected;
  GtkTreeIter parent;
  gboolean is_window;
  gboolean is_vertical;
  gboolean is_horizontal;

  if (!gtk_tree_selection_get_selected (selection, NULL, &selected))
    return;

  gtk_tree_model_get (model, &selected, ENVIRONMENTS_IS_WINDOW_COLUMN, &is_window,
  					ENVIRONMENTS_IS_VERTICAL_COLUMN, &is_vertical,
  					ENVIRONMENTS_IS_HORIZONTAL_COLUMN, &is_horizontal, -1);

  if (is_window)
    gtk_tree_model_iter_parent (model, &parent, &selected);
  else
    parent = selected;

  gtk_tree_store_remove (GTK_TREE_STORE (model), &parent);

  gtk_widget_set_sensitive (remove_environment_button, FALSE);
  gtk_widget_set_sensitive (add_enclosure_button, FALSE);
  gtk_widget_set_sensitive (widget, FALSE);
  gtk_widget_set_sensitive (add_window_button, TRUE);
  gtk_widget_set_sensitive (delete_window_button, FALSE);
}

void
add_enclosure_clicked (GtkWidget *widget, gpointer data)
{
  GtkBuilder *builder = GTK_BUILDER (data);
  GtkTreeView *treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder, "environments_treeview"));
  GtkWidget *delete_enclosure_button = GTK_WIDGET (gtk_builder_get_object (builder, "delete_enclosure_button"));
  GtkWidget *add_window_button = GTK_WIDGET (gtk_builder_get_object (builder, "add_window_button"));
  GtkWidget *delete_window_button = GTK_WIDGET (gtk_builder_get_object (builder, "delete_window_button"));
  GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
  GtkTreeModel *model = gtk_tree_view_get_model (treeview);
  GtkTreeViewColumn *column;
  GtkTreePath *path;
  GtkTreeIter selected;
  GtkTreeIter parent;
  GtkTreeIter iter;
  gboolean is_window;
  gboolean is_vertical;
  gboolean is_horizontal;

  if (!gtk_tree_selection_get_selected (selection, NULL, &selected))
    return;

  gtk_tree_model_get (model, &selected, ENVIRONMENTS_IS_WINDOW_COLUMN, &is_window,
  					ENVIRONMENTS_IS_VERTICAL_COLUMN, &is_vertical,
  					ENVIRONMENTS_IS_HORIZONTAL_COLUMN, &is_horizontal, -1);

  if (is_window) {
    gtk_tree_model_iter_parent (model, &parent, &selected);
    gtk_tree_model_iter_parent (model, &parent, &parent); /* ¿Funcionará? */
  } else if (is_vertical || is_horizontal)
    gtk_tree_model_iter_parent (model, &parent, &selected);
  else
    parent = selected;

  gtk_tree_store_append (GTK_TREE_STORE (model), &iter, &parent);
  gtk_tree_store_set (GTK_TREE_STORE (model), &iter, ENVIRONMENTS_ENVIRONMENT_COLUMN, "Nuevo cerramiento",
 		      ENVIRONMENTS_WIDTH_EDITABLE_COLUMN, TRUE,
		      ENVIRONMENTS_HEIGHT_EDITABLE_COLUMN, TRUE, -1);

  path = gtk_tree_model_get_path (model, &parent);
  gtk_tree_view_expand_to_path (treeview, path);

  path = gtk_tree_model_get_path (model, &iter);
  column = gtk_tree_view_get_column (treeview, ENVIRONMENTS_ENVIRONMENT_COLUMN);
  gtk_tree_view_set_cursor (treeview, path, column, TRUE);

  gtk_tree_path_free (path);

  gtk_widget_set_sensitive (delete_enclosure_button, TRUE);
  gtk_widget_set_sensitive (add_window_button, TRUE);
  gtk_widget_set_sensitive (delete_window_button, FALSE);
}

void
remove_environment_clicked (GtkWidget *widget, gpointer data)
{
  GtkBuilder *builder = GTK_BUILDER (data);
  GtkTreeView *treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder, "environments_treeview"));
  GtkWidget *add_enclosure_button = GTK_WIDGET (gtk_builder_get_object (builder, "add_enclosure_button"));
  GtkWidget *delete_enclosure_button = GTK_WIDGET (gtk_builder_get_object (builder, "delete_enclosure_button"));
  GtkWidget *add_window_button = GTK_WIDGET (gtk_builder_get_object (builder, "add_window_button"));
  GtkWidget *delete_window_button = GTK_WIDGET (gtk_builder_get_object (builder, "delete_window_button"));
  GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
  GtkTreeModel *model = gtk_tree_view_get_model (treeview);
  GtkTreeIter selected;
  GtkTreeIter iter;

  if (!gtk_tree_selection_get_selected (selection, NULL, &selected))
    return;

  if (!gtk_tree_model_iter_parent (model, &iter, &selected))
    iter = selected;

  gtk_tree_store_remove (GTK_TREE_STORE (model), &iter);

  gtk_widget_set_sensitive (add_enclosure_button, FALSE);
  gtk_widget_set_sensitive (delete_enclosure_button, FALSE);
  gtk_widget_set_sensitive (add_window_button, FALSE);
  gtk_widget_set_sensitive (delete_window_button, FALSE);
  gtk_widget_set_sensitive (widget, FALSE);
}

void
new_environment_clicked (GtkWidget *widget, gpointer data)
{
  GtkTreeIter parent;
  GtkTreeIter child;
  GtkTreeIter iter;
  GtkTreePath *path;
  GtkTreeViewColumn *column;
  GtkBuilder *builder = GTK_BUILDER (data);
  GtkTreeView *treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder, "environments_treeview"));
  GtkWidget *add_enclosure_button = GTK_WIDGET (gtk_builder_get_object (builder, "add_enclosure_button"));
  GtkWidget *delete_enclosure_button = GTK_WIDGET (gtk_builder_get_object (builder, "delete_enclosure_button"));
  GtkWidget *add_window_button = GTK_WIDGET (gtk_builder_get_object (builder, "add_window_button"));
  GtkWidget *delete_window_button = GTK_WIDGET (gtk_builder_get_object (builder, "delete_window_button"));
  GtkTreeModel *model = gtk_tree_view_get_model (treeview);

  g_object_ref (model);
  gtk_tree_view_set_model (treeview, NULL);

  gtk_tree_store_append (GTK_TREE_STORE (model), &parent, NULL);
  gtk_tree_store_set (GTK_TREE_STORE (model), &parent, ENVIRONMENTS_ENVIRONMENT_COLUMN, "Nuevo ambiente", -1);

  gtk_tree_view_set_model (treeview, model);
  g_object_unref (model);

  path = gtk_tree_model_get_path (model, &parent);
  gtk_tree_view_expand_to_path (treeview, path);

  column = gtk_tree_view_get_column (treeview, ENVIRONMENTS_ENVIRONMENT_COLUMN);
  gtk_tree_view_set_cursor (treeview, path, column, TRUE);

  gtk_tree_path_free (path);

  gtk_widget_set_sensitive (add_enclosure_button, TRUE);
  gtk_widget_set_sensitive (delete_enclosure_button, FALSE);
  gtk_widget_set_sensitive (add_window_button, FALSE);
  gtk_widget_set_sensitive (delete_window_button, FALSE);
  gtk_widget_set_sensitive (widget, TRUE);
}

void
delete_material_clicked (GtkWidget *widget, gpointer data)
{
  GtkBuilder *builder = GTK_BUILDER (data);
  GtkTreeView *treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder, "enclosures_treeview"));
  GtkWidget *remove_enclosure_button = GTK_WIDGET (gtk_builder_get_object (builder, "remove_enclosure_button"));
  GtkWidget *add_material_button = GTK_WIDGET (gtk_builder_get_object (builder, "add_material_button"));
  GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
  GtkTreeModel *model = gtk_tree_view_get_model (treeview);
  GtkTreeIter selected;
  GtkTreeIter iter;

  if (!(gtk_tree_selection_get_selected (selection, NULL, &selected) &&
        gtk_tree_model_iter_parent (model, &iter, &selected)))
    return;

  gtk_tree_store_remove (GTK_TREE_STORE (model), &selected);

  gtk_widget_set_sensitive (remove_enclosure_button, FALSE);
  gtk_widget_set_sensitive (add_material_button, FALSE);
  gtk_widget_set_sensitive (widget, FALSE);
}

void
add_material_clicked (GtkWidget *widget, gpointer data)
{
  GtkBuilder *builder = GTK_BUILDER (data);
  GtkTreeView *treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder, "enclosures_treeview"));
  GtkWidget *delete_material_button = GTK_WIDGET (gtk_builder_get_object (builder, "delete_material_button"));
  GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
  GtkTreeModel *model = gtk_tree_view_get_model (treeview);
  GtkTreeViewColumn *column;
  GtkTreePath *path;
  GtkTreeIter selected;
  GtkTreeIter parent;
  GtkTreeIter child;

  if (!gtk_tree_selection_get_selected (selection, NULL, &selected))
    return;

  if (!gtk_tree_model_iter_parent (model, &parent, &selected))
    parent = selected;

  gtk_tree_store_append (GTK_TREE_STORE (model), &child, &parent);
  gtk_tree_store_set (GTK_TREE_STORE (model), &child, ENCLOSURES_ENCLOSURE_COLUMN, "Material no catalogado",
  						      ENCLOSURES_THICKNESS_EDITABLE_COLUMN, TRUE,
						      ENCLOSURES_BOTH_SIDES_ACTIVATABLE_COLUMN, TRUE, -1);

  path = gtk_tree_model_get_path (model, &parent);
  gtk_tree_view_expand_to_path (treeview, path);

  path = gtk_tree_model_get_path (model, &child);
  column = gtk_tree_view_get_column (treeview, ENCLOSURES_ENCLOSURE_COLUMN);
  gtk_tree_view_set_cursor (treeview, path, column, TRUE);

  gtk_tree_path_free (path);

  gtk_widget_set_sensitive (delete_material_button, TRUE);
}

void
remove_enclosure_clicked (GtkWidget *widget, gpointer data)
{
  GtkBuilder *builder = GTK_BUILDER (data);
  GtkTreeView *treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder, "enclosures_treeview"));
  GtkWidget *add_material_button = GTK_WIDGET (gtk_builder_get_object (builder, "add_material_button"));
  GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
  GtkTreeModel *model = gtk_tree_view_get_model (treeview);
  GtkTreeIter selected;
  GtkTreeIter iter;

  if (!gtk_tree_selection_get_selected (selection, NULL, &selected))
    return;

  if (!gtk_tree_model_iter_parent (model, &iter, &selected))
    iter = selected;

  gtk_tree_store_remove (GTK_TREE_STORE (model), &iter);

  gtk_widget_set_sensitive (add_material_button, FALSE);
  gtk_widget_set_sensitive (widget, FALSE);
}

void
new_enclosure_clicked (GtkWidget *widget, gpointer data)
{
  GtkTreeIter iter;
  GtkTreePath *path;
  GtkTreeViewColumn *column;
  GtkBuilder *builder = GTK_BUILDER (data);
  GtkTreeView *treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder, "enclosures_treeview"));
  GtkWidget *add_material_button = GTK_WIDGET (gtk_builder_get_object (builder, "add_material_button"));
  GtkWidget *remove_enclosure_button = GTK_WIDGET (gtk_builder_get_object (builder, "remove_enclosure_button"));
  GtkTreeModel *model = gtk_tree_view_get_model (treeview);

  gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL);
  gtk_tree_store_set (GTK_TREE_STORE (model), &iter, ENCLOSURES_ENCLOSURE_COLUMN, "Nuevo cerramiento",
  						     ENCLOSURES_TYPE_EDITABLE_COLUMN, TRUE,
  						     ENCLOSURES_THICKNESS_EDITABLE_COLUMN, TRUE,
  						     ENCLOSURES_TRANSMITTANCE_EDITABLE_COLUMN, TRUE, -1);

  path = gtk_tree_model_get_path (model, &iter);
  column = gtk_tree_view_get_column (treeview, ENCLOSURES_ENCLOSURE_COLUMN);
  gtk_tree_view_set_cursor (treeview, path, column, TRUE);

  gtk_tree_path_free (path);

  gtk_widget_set_sensitive (add_material_button, TRUE);
  gtk_widget_set_sensitive (remove_enclosure_button, TRUE);
}

void
remove_material_clicked (GtkWidget *widget, gpointer data)
{
  GtkBuilder *builder = GTK_BUILDER (data);
  GtkTreeView *treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder, "materials_treeview"));
  GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
  GtkTreeModel *model = gtk_tree_view_get_model (treeview);
  GtkTreeIter iter;

  if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
    return;

  gtk_list_store_remove (GTK_LIST_STORE (model), &iter);

  gtk_widget_set_sensitive (widget, FALSE);
}

void
remove_location_clicked (GtkWidget *widget, gpointer data)
{
  GtkBuilder *builder = GTK_BUILDER (data);
  GtkTreeView *treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder, "locations_treeview"));
  GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
  GtkTreeModel *model = gtk_tree_view_get_model (treeview);
  GtkTreeIter iter;

  if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
    return;

  gtk_list_store_remove (GTK_LIST_STORE (model), &iter);

  gtk_widget_set_sensitive (widget, FALSE);
}

void
location_changed (GtkWidget *widget, gpointer data)
{
  gchar *location = gtk_combo_box_get_active_text (GTK_COMBO_BOX (widget));
  sqlite3_stmt *statement;

  if (sql_statement (&statement, "select maxima, minima from temperaturas where ciudad = '%s';", location)) {
    GtkBuilder *builder = GTK_BUILDER (data);
    GtkSpinButton *maximum_temperature_spinbutton = GTK_SPIN_BUTTON (gtk_builder_get_object (builder, "maximum_temperature_spinbutton"));
    GtkSpinButton *minimum_temperature_spinbutton = GTK_SPIN_BUTTON (gtk_builder_get_object (builder, "minimum_temperature_spinbutton"));
    gdouble maximum = sqlite3_column_double (statement, 0);
    gdouble minimum = sqlite3_column_double (statement, 1);

    gtk_spin_button_set_value (maximum_temperature_spinbutton, maximum);
    gtk_spin_button_set_value (minimum_temperature_spinbutton, minimum);
  }

  g_free (location);
}

void
environments_cursor_changed (GtkWidget *widget, gpointer data)
{
  GtkBuilder *builder = GTK_BUILDER (data);
  GtkTreeView *treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder, "environments_treeview"));
  GtkWidget *remove_environment_button = GTK_WIDGET (gtk_builder_get_object (builder, "remove_environment_button"));
  GtkWidget *add_enclosure_button = GTK_WIDGET (gtk_builder_get_object (builder, "add_enclosure_button"));
  GtkWidget *delete_enclosure_button = GTK_WIDGET (gtk_builder_get_object (builder, "delete_enclosure_button"));
  GtkWidget *add_window_button = GTK_WIDGET (gtk_builder_get_object (builder, "add_window_button"));
  GtkWidget *delete_window_button = GTK_WIDGET (gtk_builder_get_object (builder, "delete_window_button"));
  GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
  GtkTreeModel *model = gtk_tree_view_get_model (treeview);
  GtkTreeIter selected;
  GtkTreeIter parent;
  gboolean is_vertical;
  gboolean is_horizontal;
  gboolean is_window;
  gint windows;

  if (!gtk_tree_selection_get_selected (selection, NULL, &selected))
    return;

  gtk_tree_model_get (model, &selected, ENVIRONMENTS_IS_VERTICAL_COLUMN, &is_vertical,
  		      		        ENVIRONMENTS_IS_HORIZONTAL_COLUMN, &is_horizontal,
  		      		        ENVIRONMENTS_IS_WINDOW_COLUMN, &is_window, -1);

  if (is_window)
    gtk_tree_model_iter_parent (model, &parent, &selected);
  else
    parent = selected;

  gtk_tree_model_get (model, &parent, ENVIRONMENTS_WINDOWS_COLUMN, &windows, -1);

  gtk_widget_set_sensitive (remove_environment_button, TRUE);
  gtk_widget_set_sensitive (add_enclosure_button, TRUE);
  gtk_widget_set_sensitive (delete_enclosure_button, is_vertical || is_horizontal || is_window);
  gtk_widget_set_sensitive (add_window_button, windows < MAXIMUM_WINDOWS && (is_window || is_vertical));
  gtk_widget_set_sensitive (delete_window_button, is_window);
}

void
enclosures_cursor_changed (GtkWidget *widget, gpointer data)
{
  GtkBuilder *builder = GTK_BUILDER (data);
  GtkTreeView *treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder, "enclosures_treeview"));
  GtkWidget *remove_enclosure_button = GTK_WIDGET (gtk_builder_get_object (builder, "remove_enclosure_button"));
  GtkWidget *add_material_button = GTK_WIDGET (gtk_builder_get_object (builder, "add_material_button"));
  GtkWidget *delete_material_button = GTK_WIDGET (gtk_builder_get_object (builder, "delete_material_button"));
  GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
  GtkTreeModel *model = gtk_tree_view_get_model (treeview);
  GtkTreeIter selected;
  gchar *path;

  if (!gtk_tree_selection_get_selected (selection, NULL, &selected))
    return;

  path = gtk_tree_model_get_string_from_iter (model, &selected);

  gtk_widget_set_sensitive (delete_material_button, !is_parent (path));
  gtk_widget_set_sensitive (remove_enclosure_button, TRUE);
  gtk_widget_set_sensitive (add_material_button, TRUE);

  g_free (path);
}

void
materials_cursor_changed (GtkWidget *widget, gpointer data)
{
  GtkBuilder *builder = GTK_BUILDER (data);
  GtkWidget *remove_material_button = GTK_WIDGET (gtk_builder_get_object (builder, "remove_material_button"));

  gtk_widget_set_sensitive (remove_material_button, TRUE);
}

void
locations_cursor_changed (GtkWidget *widget, gpointer data)
{
  GtkBuilder *builder = GTK_BUILDER (data);
  GtkWidget *remove_location_button = GTK_WIDGET (gtk_builder_get_object (builder, "remove_location_button"));

  gtk_widget_set_sensitive (remove_location_button, TRUE);
}

void
create_tags (GtkTextBuffer *buffer)
{
  gtk_text_buffer_create_tag (buffer, "worksheet", "family", "monospace", NULL);
  gtk_text_buffer_create_tag (buffer, "title", "weight", PANGO_WEIGHT_BOLD, "scale", PANGO_SCALE_XX_LARGE, NULL);
  gtk_text_buffer_create_tag (buffer, "heading", "weight", PANGO_WEIGHT_BOLD, "scale", PANGO_SCALE_X_LARGE, NULL);
  gtk_text_buffer_create_tag (buffer, "abstract", "scale", PANGO_SCALE_SMALL, NULL);
  gtk_text_buffer_create_tag (buffer, "center", "justification", GTK_JUSTIFY_CENTER, NULL);
  already_tags = 1;
}

void
switch_page (GtkWidget *widget, GtkNotebookPage *page, guint number, gpointer data)
{
  if (number != SUMMARY_TAB)
    return;

  GtkBuilder *builder = GTK_BUILDER (data);
  GtkWidget *summary_textview = GTK_WIDGET (gtk_builder_get_object (builder, "summary_textview"));
  GtkWidget *build_entry = GTK_WIDGET (gtk_builder_get_object (builder, "build_entry"));
  GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (summary_textview));
  GtkTextIter iter;
  const gchar *build;

  build = gtk_entry_get_text (GTK_ENTRY (build_entry));

  gtk_text_buffer_set_text (buffer, "", 0);
  gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);

  if (!already_tags)
    create_tags (buffer);

  gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, "Cálculo de balance térmico de invierno\n", -1, "title", "center", NULL);
  gtk_text_buffer_insert (buffer, &iter, "\n", -1);
  gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, "Este programa calcula el balance térmico en forma profesional. El balance "
    "térmico es la potencia que hay que suministrar a un local para obtener el bienestar térmico en condiciones determinadas. Las "
    "condiciones utilizadas se extienden a las necesidades de calefaccionar, es decir se calculan en régimen estacionario desfavorable "
    "a la calefacción (invierno, en este caso).\n", -1, "abstract", NULL);
  gtk_text_buffer_insert (buffer, &iter, "\n", -1);
  gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, build, -1, "title", NULL);
  gtk_text_buffer_insert (buffer, &iter, "\n", -1);
  gtk_text_buffer_insert (buffer, &iter, "\n", -1);
  gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, "Cerramientos utilizados\n", -1, "heading", NULL);
  gtk_text_buffer_insert (buffer, &iter, "\n", -1);

  gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
    "?-------+------+------+------+------?\n"
    "| Total | 10,0 | 10,0 | 10,0 | 10,0 |\n"
    "| Total | 10,0 | 10,0 | 10,0 | 10,0 |\n"
    "| Total | 10,0 | 10,0 | 10,0 | 10,0 |\n"
    "| Total | 10,0 | 10,0 | 10,0 | 10,0 |\n"
    "?-------+------+------+------+------?\n",
    -1, "worksheet", NULL);

  GtkWidget *print_button = GTK_WIDGET (gtk_builder_get_object (builder, "print_button"));

  gtk_widget_set_sensitive (print_button, TRUE);
}

#ifndef G_OS_WIN32
int
main (int argc, char *argv[])
#else
int WINAPI
WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
#endif
{
  GtkBuilder *builder;
  GtkWidget *main_window;
  GtkWidget *build_entry;
  GtkWidget *environments_treeview;
  GtkWidget *enclosures_treeview;
  GtkWidget *materials_treeview;
  GtkWidget *locations_treeview;
  GtkWidget *locations_combobox;
  GtkTreeModel *environments_model;
  GtkTreeModel *enclosures_model;
  GtkTreeModel *materials_model;
  GtkTreeModel *locations_model;
  GtkTreeModel *builds_model;
  GtkCellRenderer *renderer;
  GtkEntryCompletion *completion;
  GError *error;

#ifndef G_OS_WIN32
  gtk_init (&argc, &argv);
#else
  gtk_init (NULL, NULL);
#endif

  open_database (DATABASE);

  error = NULL;
  builder = gtk_builder_new ();
  gtk_builder_add_from_file (builder, INTERFACE, &error);

  if (error) {
    show_error_dialog ("Error de UI", "Error al abrir la interfaz con el usuario", error->message);
    sqlite3_close (database_handler);
    return (1);
  }

  /* get widgets */
  main_window = GTK_WIDGET (gtk_builder_get_object (builder, "main_window"));
  environments_treeview = GTK_WIDGET (gtk_builder_get_object (builder, "environments_treeview"));
  enclosures_treeview = GTK_WIDGET (gtk_builder_get_object (builder, "enclosures_treeview"));
  materials_treeview = GTK_WIDGET (gtk_builder_get_object (builder, "materials_treeview"));
  locations_treeview = GTK_WIDGET (gtk_builder_get_object (builder, "locations_treeview"));
  locations_combobox = GTK_WIDGET (gtk_builder_get_object (builder, "locations_combobox"));
  build_entry = GTK_WIDGET (gtk_builder_get_object (builder, "build_entry"));

  /* column */
  renderer = gtk_cell_renderer_text_new ();

  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (locations_combobox), renderer, TRUE);
  gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (locations_combobox), renderer, "text", 0, NULL);

  /* completion */
  completion = gtk_entry_completion_new ();
  gtk_entry_completion_set_text_column (completion, 0);
  gtk_entry_completion_set_inline_completion (completion, TRUE);
  gtk_entry_completion_set_inline_selection (completion, TRUE);
  gtk_entry_completion_set_popup_completion (completion, TRUE);
  gtk_entry_completion_set_popup_set_width (completion, TRUE);
  gtk_entry_completion_set_popup_single_match (completion, TRUE);
  gtk_entry_completion_set_minimum_key_length (completion, 0);
  gtk_entry_set_completion (GTK_ENTRY (build_entry), completion);

  /* create models */
  environments_model = create_environments_tree_model ();
  enclosures_model = create_enclosures_tree_model ();
  materials_model = create_materials_list_model ();
  locations_model = create_locations_list_model ();
  builds_model = create_builds_list_model ();

  /* add data */

  /*
   * 00:	ambiente:	ENVIRONMENTS_ENVIRONMENT_COLUMN:	P
   * 01:	cerramiento:	ENVIRONMENTS_ENVIRONMENT_COLUMN:	C
   * 02:	exposicion:	ENVIRONMENTS_GROUP_COLUMN:		P
   * 03:	grupo:		ENVIRONMENTS_GROUP_COLUMN:		C
   * 04:	x:		ENVIRONMENTS_WIDTH_COLUMN:		P
   * 05:	y:		ENVIRONMENTS_HEIGHT_COLUMN:		P
   * 06:	x:		ENVIRONMENTS_WIDTH_COLUMN:		C
   * 07:	y:		ENVIRONMENTS_HEIGHT_COLUMN:		C
   * 08:	volumen:	ENVIRONMENTS_AREA_COLUMN:		P
   * 09:	superficie:	ENVIRONMENTS_AREA_COLUMN:		C
   * 10:	doble:		ENVIRONMENTS_SAME_FLOOR_CEIL_COLUMN:	C
   * 11:	balance:	ENVIRONMENTS_HEAT_COLUMN:		P
   * 12:	calor:		ENVIRONMENTS_HEAT_COLUMN:		C
   */

  set_environments_tree (environments_model, 
    "select "
    "  ac.ambiente as ambiente, "
    "  ac.cerramiento as cerramiento, "
    "  '<span color=\"grey\">No implementado</span>' as exposicion, "
    "  ac.grupo as grupo, "
    "  null as x, "
    "  null as y, "
    "  ac.x as x, "
    "  ac.y as y, "
    "  '<span color=\"grey\">v = ' || v.volumen || ' m³</span>' as volumen, "
    "  t.superficie as superficie, "
    "  ac.doble as doble, "
    "  '<span color=\"red\"><b>' || round (i.balance) || '</b></span>' as balance, "
    "  '<span color=\"red\">' || round (w.calor) || '</span>' as calor "
    "from "
    "  ambientes_cerramientos ac, "
    "  cerramientos c, "
    "  volumenes v, "
    "  calores w, "
    "  transmitancias t, "
    "  infiltraciones i "
    "where "
    "  ac.cerramiento = c.cerramiento and "
    "  w.id = ac.id and "
    "  t.id = ac.id and "
    "  ac.obra = v.obra and "
    "  ac.ambiente = v.ambiente and "
    "  ac.obra = i.obra and "
    "  ac.ambiente = i.ambiente and "
    "  ac.obra = 'Semipiso modelo' "
    "group by "
    "  ac.id "
    "order by "
    "  ac.ambiente, "
    "  c.cerramiento;");

  /*
   * 00:	cerramiento:	ENCLOSURES_ENCLOSURE_COLUMN:		P
   * 01:	material:	ENCLOSURES_ENCLOSURE_COLUMN:		C
   * 02:	tipo:		ENCLOSURES_TYPE_COLUMN:			P
   * 03:	espesor:	ENCLOSURES_THICKNESS_COLUMN:		P
   * 04:	espesor: 	ENCLOSURES_THICKNESS_COLUMN:		C
   * 05:	transmitancia:	ENCLOSURES_TRANSMITTANCE_COLUMN:	P
   * 06:	conductividad:	ENCLOSURES_TRANSMITTANCE_COLUMN:	C
   * 07:	doble:		ENCLOSURES_BOTH_SIDES_COLUMN:		C
   */

  set_enclosures_tree (enclosures_model,
    "select  "
    "  c.cerramiento as cerramiento, "
    "  m.material || case when m.densidad is not null then ' de ' || m.densidad || ' kg/m³' else '' end as material, "
    "  c.tipo as tipo, "
    "  '<span color=\"grey\">(SUMA) = ' || round (rb.se, 3) || '</span>' as espesor, "
    "  cm.espesor as espesor, "
    "  '<span color=\"grey\">1/(SUMA) = ' || round (1 / rb.sR, 3) || '</span>' as transmitancia, "
    "  '<span color=\"grey\">? = ' || m.conductividad || ' W/m°C</span>' as conductividad, "
    "  cm.doble as doble "
    "from "
    "  cerramientos_materiales cm, "
    "  cerramientos c, "
    "  materiales m, "
    "  (select "
    "     ra.cerramiento as cerramiento, "
    "     sum (ra.espesor) as se, "
    "     sum (ra.resistencia) as sR "
    "   from "
    "     (select "
    "        c.cerramiento as cerramiento, "
    "	     (cm.espesor + (cm.espesor * cm.doble)) as espesor, "
    "        (cm.espesor + (cm.espesor * cm.doble)) / 100 / m.conductividad as resistencia "
    "      from "
    "        cerramientos_materiales cm, "
    "        cerramientos c, "
    "        materiales m "
    "      where "
    "        m.material = cm.material and "
    "        (m.densidad = cm.densidad or cm.densidad is null) and "
    "         c.cerramiento = cm.cerramiento) ra "
    "      group by ra.cerramiento) rb "
    "where "
    "  rb.cerramiento = c.cerramiento and "
    "  m.material = cm.material and "
    "  (m.densidad = cm.densidad or cm.densidad is null) and "
    "  c.cerramiento = cm.cerramiento "
    "order by cerramiento;");

  set_data_list (enclosures_model,
    "select  "
    "   cerramiento, "
    "   tipo, "
    "   espesor, "
    "   transmitancia, "
    "   0, "
    "   null "
    "from cerramientos c "
    "where  not exists (select 1 from cerramientos_materiales cm where cm.cerramiento = c.cerramiento) "
    "order by cerramiento;");

  set_data_list (materials_model, "select material, densidad, conductividad from materiales order by material;");
  set_data_list (locations_model, "select ciudad, minima, maxima from temperaturas order by ciudad;");
  set_data_list (builds_model, "select obra from obras order by obra;");

 /* attach models */
  gtk_tree_view_set_model (GTK_TREE_VIEW (environments_treeview), environments_model);
  gtk_tree_view_set_model (GTK_TREE_VIEW (enclosures_treeview), enclosures_model);
  gtk_tree_view_set_model (GTK_TREE_VIEW (materials_treeview), materials_model);
  gtk_tree_view_set_model (GTK_TREE_VIEW (locations_treeview), locations_model);
  gtk_combo_box_set_model (GTK_COMBO_BOX (locations_combobox), locations_model);
  gtk_entry_completion_set_model (completion, builds_model);

  /* unreference models */
  g_object_unref (environments_model);
  g_object_unref (enclosures_model);
  g_object_unref (materials_model);
  g_object_unref (locations_model);
  g_object_unref (builds_model);

  /* add columns */
  add_environments_columns (GTK_TREE_VIEW (environments_treeview));
  add_enclosures_columns (GTK_TREE_VIEW (enclosures_treeview));
  add_materials_columns (GTK_TREE_VIEW (materials_treeview));
  add_locations_columns (GTK_TREE_VIEW (locations_treeview));

#ifndef G_OS_WIN32
  gtk_builder_connect_signals (builder, builder);
#else
  /* En Windows el mecanismo de introspección de GModule para buscar las retrollamadas en la tabla de símbolos no funciona aún cuando
   * la plataforma incluye soporte de GModule. En este caso hay que conectar las señales manualmente.
   */
  GtkWidget *minimum_temperature_spinbutton = GTK_WIDGET (gtk_builder_get_object (builder, "minimum_temperature_spinbutton"));
  GtkWidget *maximum_temperature_spinbutton = GTK_WIDGET (gtk_builder_get_object (builder, "maximum_temperature_spinbutton"));
  GtkWidget *new_location_button = GTK_WIDGET (gtk_builder_get_object (builder, "new_location_button"));
  GtkWidget *remove_location_button = GTK_WIDGET (gtk_builder_get_object (builder, "remove_location_button"));
  GtkWidget *new_material_button = GTK_WIDGET (gtk_builder_get_object (builder, "new_material_button"));
  GtkWidget *remove_material_button = GTK_WIDGET (gtk_builder_get_object (builder, "remove_material_button"));
  GtkWidget *new_enclosure_button = GTK_WIDGET (gtk_builder_get_object (builder, "new_enclosure_button"));
  GtkWidget *remove_enclosure_button = GTK_WIDGET (gtk_builder_get_object (builder, "remove_enclosure_button"));
  GtkWidget *add_material_button = GTK_WIDGET (gtk_builder_get_object (builder, "add_material_button"));
  GtkWidget *delete_material_button = GTK_WIDGET (gtk_builder_get_object (builder, "delete_material_button"));
  GtkWidget *new_environment_button = GTK_WIDGET (gtk_builder_get_object (builder, "new_environment_button"));
  GtkWidget *remove_environment_button = GTK_WIDGET (gtk_builder_get_object (builder, "remove_environment_button"));
  GtkWidget *add_enclosure_button = GTK_WIDGET (gtk_builder_get_object (builder, "add_enclosure_button"));
  GtkWidget *delete_enclosure_button = GTK_WIDGET (gtk_builder_get_object (builder, "delete_enclosure_button"));
  GtkWidget *add_window_button = GTK_WIDGET (gtk_builder_get_object (builder, "add_window_button"));
  GtkWidget *delete_window_button = GTK_WIDGET (gtk_builder_get_object (builder, "delete_window_button"));
  GtkWidget *print_button = GTK_WIDGET (gtk_builder_get_object (builder, "print_button"));
  GtkWidget *save_button = GTK_WIDGET (gtk_builder_get_object (builder, "save_button"));
  GtkWidget *notebook = GTK_WIDGET (gtk_builder_get_object (builder, "notebook"));
  GtkWidget *height_spinbutton = GTK_WIDGET (gtk_builder_get_object (builder, "height_spinbutton"));
  GtkWidget *same_floor_ceil_checkbutton = GTK_WIDGET (gtk_builder_get_object (builder, "same_floor_ceil_checkbutton"));

  g_signal_connect (main_window, "delete-event", G_CALLBACK (main_window_delete_event), builder);
  g_signal_connect (enclosures_treeview, "cursor-changed", G_CALLBACK (enclosures_cursor_changed), builder);
  g_signal_connect (environments_treeview, "cursor-changed", G_CALLBACK (environments_cursor_changed), builder);
  g_signal_connect (materials_treeview, "cursor-changed", G_CALLBACK (materials_cursor_changed), builder);
  g_signal_connect (locations_treeview, "cursor-changed", G_CALLBACK (locations_cursor_changed), builder);
  g_signal_connect (locations_combobox, "changed", G_CALLBACK (location_changed), builder);
  g_signal_connect (new_location_button, "clicked", G_CALLBACK (new_location_clicked), builder);
  g_signal_connect (remove_location_button, "clicked", G_CALLBACK (remove_location_clicked), builder);
  g_signal_connect (new_material_button, "clicked", G_CALLBACK (new_material_clicked), builder);
  g_signal_connect (remove_material_button, "clicked", G_CALLBACK (remove_material_clicked), builder);
  g_signal_connect (new_enclosure_button, "clicked", G_CALLBACK (new_enclosure_clicked), builder);
  g_signal_connect (remove_enclosure_button, "clicked", G_CALLBACK (remove_enclosure_clicked), builder);
  g_signal_connect (add_material_button, "clicked", G_CALLBACK (add_material_clicked), builder);
  g_signal_connect (delete_material_button, "clicked", G_CALLBACK (delete_material_clicked), builder);
  g_signal_connect (new_environment_button, "clicked", G_CALLBACK (new_environment_clicked), builder);
  g_signal_connect (remove_environment_button, "clicked", G_CALLBACK (remove_environment_clicked), builder);
  g_signal_connect (add_enclosure_button, "clicked", G_CALLBACK (add_enclosure_clicked), builder);
  g_signal_connect (delete_enclosure_button, "clicked", G_CALLBACK (delete_enclosure_clicked), builder);
  g_signal_connect (add_window_button, "clicked", G_CALLBACK (add_window_clicked), builder);
  g_signal_connect (delete_window_button, "clicked", G_CALLBACK (delete_window_clicked), builder);
  g_signal_connect (print_button, "clicked", G_CALLBACK (print_clicked), builder);
  g_signal_connect (save_button, "clicked", G_CALLBACK (save_clicked), builder);
  g_signal_connect (notebook, "switch-page", G_CALLBACK (switch_page), builder);
  g_signal_connect (height_spinbutton, "query-tooltip", G_CALLBACK (query_height_tooltip), NULL);
  g_signal_connect (same_floor_ceil_checkbutton, "query-tooltip", G_CALLBACK (query_same_floor_ceil_tooltip), NULL);
#endif

  gtk_widget_show_all (main_window);
  
  gtk_main ();

  return (0);
}
